#include <AReVi/arClass.h>

#include <AReVi/arSystem.h>
#include <AReVi/activity.h>
#include <AReVi/Lib3D/boundingBox3D.h>
#include <AReVi/Lib3D/viewer3D.h>
#include <AReVi/Lib3D/simpleInteractor.h>
using namespace AReVi;

#include "hLib/hLib.h"
using namespace hLib;

class TestSkin : public Viewer3D {
public:
  AR_CLASS(TestSkin)
  AR_CONSTRUCTOR(TestSkin)

protected:
  ArRef<SimpleInteractor> interactor;
  ArRef<Body> body;
  ArRef<KeyframeAnimation> animation;
  
  virtual bool event(ArRef<Activity> act, double dt);
};


AR_CLASS_DEF(TestSkin,Viewer3D)


TestSkin::TestSkin(ArCW& arCW)
: Viewer3D(arCW), interactor(SimpleInteractor::NEW()) {
  interactor->setRenderer(thisRef());

  assert(ArSystem::getCommandLine().size() >= 2);

  StlString str =  ArSystem::getCommandLine()[1];
  cerr << "Loading " << str << " ...\t";
  LoaderData data = HLibLoader::loadFile(str);
  if(data.failed) {
    cerr << "FAILED" << endl;
  } else {
    cerr << "done" << endl;
  }
  body = data.body;
  
  /** Est-ce qu'il y a une animation directement dans le fichier */
  if(data.animation != Animation::nullRef()) {    
    animation = data.animation;
    body->applyPose(animation->getKeyframe(0));
  }
  body->accessBodyShape()->setShowSkeleton(false);
  
  /** Est-ce qu'on a précisé une texture ? */
  if (ArSystem::getCommandLine().size() > 2) {
    str =  ArSystem::getCommandLine()[2];
    cerr << "Loading texture " << str << " ...\t" << endl;
    ArRef<URLTexture> texture = URLTexture::NEW(str, true, true);
    body->accessBodyShape()->accessSkin(0)->setTexture(texture);
  }

  /** Est-ce qu'on a précisé une animation ? */
  if(ArSystem::getCommandLine().size() > 3) {
    str =  ArSystem::getCommandLine()[3];
    cerr << "Loading animation " << str << " ...\t" << endl;
    animation = HLibLoader::loadAnimation(str);
    if(animation != Animation::nullRef()) {
      body->applyPose(animation->getKeyframe(0));
    }
  }
  
  ArRef<BoundingBox3D> bbox = BoundingBox3D::NEW();
  body->accessShape()->readBoundingBox(bbox);
  setPosition(-2 *  bbox->getMaxSize(),0,bbox->getMaxSize() / 2.0);
  setFarDistance(50.0*bbox->getMaxSize());

  /** On lance l'activité pour l'animation */
  ArRef<Activity> act = Activity::NEW(0.04);
  act->setBehavior(thisRef(), &TestSkin::event);
}

bool TestSkin::event(ArRef<Activity> act, double /*dt*/)
{
  if(animation != Animation::nullRef()) {
    body->applyPose(animation->evaluate(fmod(act->getTime(),animation->getDuration())));
    return true;
  }
  else {
    return false;
  }
}


TestSkin::~TestSkin()
{
}

ArRef<Viewer3D> viewer;

ArRef<Scheduler> simulationInit(void) {

  if(ArSystem::getCommandLine().size() < 2) {
    cerr << "usage : " << ArSystem::getCommandLine()[0];
    cerr << " skeleton_file animation_file (optional)" << endl;
    return(Scheduler::nullRef());
  }

  ArRef<Scheduler> scd = RealTimeScheduler::NEW(1e-3);

  viewer=TestSkin::NEW();
  viewer->setCloseAction(Window3D::CLOSE_LEAVE);

  unsigned int w,h;
  Renderer3D::getScreenSize(w,h);
  viewer->setWindowGeometry(w-800,0,800,800);
  viewer->setMapped(true);

  return scd;
}


int main(int argc, char** argv) {
  ArSystem arevi(argc, argv);

  hLibInit();
  TestSkin::REGISTER_CLASS();

  ArSystem::loadPlugin("Imlib2ImageLoader");
  ArSystem::loadPlugin("MagickImageLoader");

  ArSystem::simulationLoop(&simulationInit);

  return 0;
}
